<?php

namespace Plugins\AdminBasic\Http\Admin\Service;

use App\Interfaces\AdminRolePermissionsCacheInterface;
use App\Models\Model;
use App\Util\AdminAuth;
use Hyperf\Di\Annotation\Inject;
use Plugins\AdminBasic\Http\Admin\Service\RolePermissions\PermissionsCacheData;
use Plugins\AdminBasic\Models\BasicRole;
use Plugins\AdminBasic\Models\BasicRoute;
use Psr\Http\Message\UriInterface;
use Psr\SimpleCache\InvalidArgumentException;
use Sc\Util\ImitateAopProxy\AopProxyTrait;

class RolePermissionsService implements AdminRolePermissionsCacheInterface
{
    use AopProxyTrait;

    #[Inject]
    private PermissionsCacheData $cacheData;

    /**
     * 缓存权限数据
     *
     * @return void
     * @throws InvalidArgumentException
     */
    public function cache(): void
    {
        $this->cacheRouteMapping();
        $this->cacheRoleRouteIds();
    }

    public function cacheRouteMapping(): void
    {
        $routeIdMapping = BasicRoute::select(['route', 'id'])->get()
            ->groupBy('route')
            ->map(fn($v) => $v->pluck('id'))
            ->toArray();

        $this->cacheData->routeIdMapping = $routeIdMapping;
    }

    /**
     * @param BasicRole|null $role
     * @return void
     */
    public function cacheRoleRouteIds(?Model $role = null): void
    {
        if ($role) {
            $this->cacheData->roleRouteIds[$role->id] = array_values(array_unique(array_merge(...array_map(fn($v) => (array)$v, $role->route_ids))));;
            return;
        }
        $roleRouteIds = BasicRole::pluck('route_ids', 'id')->toArray();
        foreach ($roleRouteIds as &$routeIds) {
            $routeIds = array_values(array_unique(array_merge(...array_map(fn($v) => (array)$v, $routeIds ?: []))));
        }

        $this->cacheData->roleRouteIds = $roleRouteIds;
    }

    /**
     * 检测
     *
     * @param UriInterface|string $uri
     * @param array $roleIds
     * @return bool
     */
    public function check(UriInterface|string $uri, array $roleIds): bool
    {
        if (is_string($uri)) {
            $path = $ac = $uri;
        }else{
            $path = $uri->getPath();
            $ac   = $path . '?' . $uri->getQuery();
        }

        if ((!isset($this->cacheData->routeIdMapping[$path])
            && !isset($this->cacheData->routeIdMapping[$ac]))
            || AdminAuth::isSuper()) {
            return true;
        }

        $routeIds = array_merge($this->cacheData->routeIdMapping[$path] ?? [], $this->cacheData->routeIdMapping[$ac] ?? []);

        foreach ($roleIds as $role) {
            if (empty($this->cacheData->roleRouteIds[$role])) {
                continue;
            }
            if (array_intersect($routeIds, $this->cacheData->roleRouteIds[$role])) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array $roleIds
     *
     * @return array
     */
    public function getRouteIds(array $roleIds): array
    {
        $routeIds = [];
        foreach ($roleIds as $roleId) {
            $routeIds = [...$routeIds, ...$this->cacheData->roleRouteIds[$roleId] ?? []];
        }

        return $routeIds;
    }
}